#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "saverestore.h"
#include <time.h>
#include "shake.h"
#include "decals.h"
#include "player.h"
#include "weapons.h"
#include "gamerules.h"
#include "enginedef.h"

extern DLL_GLOBAL int g_fIsTraceLine;

float UTIL_WeaponTimeBase(void)
{
#ifdef CLIENT_WEAPONS
	return 0;
#else
	return gpGlobals->time;
#endif
}

static unsigned int glSeed = 0;

unsigned int seed_table[256] =
{
	28985, 27138, 26457, 9451, 17764, 10909, 28790, 8716, 6361, 4853, 17798, 21977, 19643, 20662, 10834, 20103,
	27067, 28634, 18623, 25849, 8576, 26234, 23887, 18228, 32587, 4836, 3306, 1811, 3035, 24559, 18399, 315,
	26766, 907, 24102, 12370, 9674, 2972, 10472, 16492, 22683, 11529, 27968, 30406, 13213, 2319, 23620, 16823,
	10013, 23772, 21567, 1251, 19579, 20313, 18241, 30130, 8402, 20807, 27354, 7169, 21211, 17293, 5410, 19223,
	10255, 22480, 27388, 9946, 15628, 24389, 17308, 2370, 9530, 31683, 25927, 23567, 11694, 26397, 32602, 15031,
	18255, 17582, 1422, 28835, 23607, 12597, 20602, 10138, 5212, 1252, 10074, 23166, 19823, 31667, 5902, 24630,
	18948, 14330, 14950, 8939, 23540, 21311, 22428, 22391, 3583, 29004, 30498, 18714, 4278, 2437, 22430, 3439,
	28313, 23161, 25396, 13471, 19324, 15287, 2563, 18901, 13103, 16867, 9714, 14322, 15197, 26889, 19372, 26241,
	31925, 14640, 11497, 8941, 10056, 6451, 28656, 10737, 13874, 17356, 8281, 25937, 1661, 4850, 7448, 12744,
	21826, 5477, 10167, 16705, 26897, 8839, 30947, 27978, 27283, 24685, 32298, 3525, 12398, 28726, 9475, 10208,
	617, 13467, 22287, 2376, 6097, 26312, 2974, 9114, 21787, 28010, 4725, 15387, 3274, 10762, 31695, 17320,
	18324, 12441, 16801, 27376, 22464, 7500, 5666, 18144, 15314, 31914, 31627, 6495, 5226, 31203, 2331, 4668,
	12650, 18275, 351, 7268, 31319, 30119, 7600, 2905, 13826, 11343, 13053, 15583, 30055, 31093, 5067, 761,
	9685, 11070, 21369, 27155, 3663, 26542, 20169, 12161, 15411, 30401, 7580, 31784, 8985, 29367, 20989, 14203,
	29694, 21167, 10337, 1706, 28578, 887, 3373, 19477, 14382, 675, 7033, 15111, 26138, 12252, 30996, 21409,
	25678, 18555, 13256, 23316, 22407, 16727, 991, 9236, 5373, 29402, 6117, 15241, 27715, 19291, 19888, 19847
};

unsigned int U_Random(void)
{
	glSeed *= 69069;
	glSeed += seed_table[glSeed & 0xFF];
	return (++glSeed & 0x0FFFFFFF);
}

void U_Srand(unsigned int seed)
{
	glSeed = seed_table[seed & 0xFF];
}

int UTIL_SharedRandomLong(unsigned int seed, int low, int high)
{
	U_Srand((int)seed + low + high);
	unsigned int range = high - low + 1;

	if (!(range - 1))
		return low;

	int rnum = U_Random();
	int offset = rnum % range;
	return (low + offset);
}

float UTIL_SharedRandomFloat(unsigned int seed, float low, float high)
{
	U_Srand((int)seed + *(int *)&low + *(int *)&high);
	U_Random();
	U_Random();

	unsigned range = (int)(high - low);

	if (!range)
		return low;

	int tensixrand = U_Random() & 65535;
	float offset = (float)tensixrand / 65536;
	return (low + offset * range);
}

void UTIL_ParametricRocket(entvars_t *pev, Vector vecOrigin, Vector vecAngles, edict_t *owner)
{
	pev->startpos = vecOrigin;

	TraceResult tr;
	UTIL_MakeVectors(vecAngles);
	UTIL_TraceLine(pev->startpos, pev->startpos + gpGlobals->v_forward * 8192, ignore_monsters, owner, &tr);
	pev->endpos = tr.vecEndPos;

	Vector vecTravel = pev->endpos - pev->startpos;
	float travelTime = 0;

	if (pev->velocity.Length() > 0)
		travelTime = vecTravel.Length() / pev->velocity.Length();

	pev->starttime = gpGlobals->time;
	pev->impacttime = gpGlobals->time + travelTime;
}

int g_groupmask = 0;
int g_groupop = 0;

void UTIL_SetGroupTrace(int groupmask, int op)
{
	g_groupmask = groupmask;
	g_groupop = op;

	ENGINE_SETGROUPMASK(g_groupmask, g_groupop);
}

void UTIL_UnsetGroupTrace(void)
{
	g_groupmask = 0;
	g_groupop = 0;

	ENGINE_SETGROUPMASK(0, 0);
}

UTIL_GroupTrace::UTIL_GroupTrace(int groupmask, int op)
{
	m_oldgroupmask = g_groupmask;
	m_oldgroupop = g_groupop;
	g_groupmask = groupmask;
	g_groupop = op;

	ENGINE_SETGROUPMASK(g_groupmask, g_groupop);
}

UTIL_GroupTrace::~UTIL_GroupTrace(void)
{
	g_groupmask = m_oldgroupmask;
	g_groupop = m_oldgroupop;

	ENGINE_SETGROUPMASK(g_groupmask, g_groupop);
}

TYPEDESCRIPTION gEntvarsDescription[] =
{
	DEFINE_ENTITY_FIELD(classname, FIELD_STRING),
	DEFINE_ENTITY_GLOBAL_FIELD(globalname, FIELD_STRING),
	DEFINE_ENTITY_FIELD(origin, FIELD_POSITION_VECTOR),
	DEFINE_ENTITY_FIELD(oldorigin, FIELD_POSITION_VECTOR),
	DEFINE_ENTITY_FIELD(velocity, FIELD_VECTOR),
	DEFINE_ENTITY_FIELD(basevelocity, FIELD_VECTOR),
	DEFINE_ENTITY_FIELD(movedir, FIELD_VECTOR),
	DEFINE_ENTITY_FIELD(angles, FIELD_VECTOR),
	DEFINE_ENTITY_FIELD(avelocity, FIELD_VECTOR),
	DEFINE_ENTITY_FIELD(punchangle, FIELD_VECTOR),
	DEFINE_ENTITY_FIELD(v_angle, FIELD_VECTOR),
	DEFINE_ENTITY_FIELD(fixangle, FIELD_FLOAT),
	DEFINE_ENTITY_FIELD(idealpitch, FIELD_FLOAT),
	DEFINE_ENTITY_FIELD(pitch_speed, FIELD_FLOAT),
	DEFINE_ENTITY_FIELD(ideal_yaw, FIELD_FLOAT),
	DEFINE_ENTITY_FIELD(yaw_speed, FIELD_FLOAT),
	DEFINE_ENTITY_FIELD(modelindex, FIELD_INTEGER),
	DEFINE_ENTITY_GLOBAL_FIELD(model, FIELD_MODELNAME),
	DEFINE_ENTITY_FIELD(viewmodel, FIELD_MODELNAME),
	DEFINE_ENTITY_FIELD(weaponmodel, FIELD_MODELNAME),
	DEFINE_ENTITY_FIELD(absmin, FIELD_POSITION_VECTOR),
	DEFINE_ENTITY_FIELD(absmax, FIELD_POSITION_VECTOR),
	DEFINE_ENTITY_GLOBAL_FIELD(mins, FIELD_VECTOR),
	DEFINE_ENTITY_GLOBAL_FIELD(maxs, FIELD_VECTOR),
	DEFINE_ENTITY_GLOBAL_FIELD(size, FIELD_VECTOR),
	DEFINE_ENTITY_FIELD(ltime, FIELD_TIME),
	DEFINE_ENTITY_FIELD(nextthink, FIELD_TIME),
	DEFINE_ENTITY_FIELD(solid, FIELD_INTEGER),
	DEFINE_ENTITY_FIELD(movetype, FIELD_INTEGER),
	DEFINE_ENTITY_FIELD(skin, FIELD_INTEGER),
	DEFINE_ENTITY_FIELD(body, FIELD_INTEGER),
	DEFINE_ENTITY_FIELD(effects, FIELD_INTEGER),
	DEFINE_ENTITY_FIELD(gravity, FIELD_FLOAT),
	DEFINE_ENTITY_FIELD(friction, FIELD_FLOAT),
	DEFINE_ENTITY_FIELD(light_level, FIELD_FLOAT),
	DEFINE_ENTITY_FIELD(frame, FIELD_FLOAT),
	DEFINE_ENTITY_FIELD(scale, FIELD_FLOAT),
	DEFINE_ENTITY_FIELD(sequence, FIELD_INTEGER),
	DEFINE_ENTITY_FIELD(animtime, FIELD_TIME),
	DEFINE_ENTITY_FIELD(framerate, FIELD_FLOAT),
	DEFINE_ENTITY_FIELD(controller, FIELD_INTEGER),
	DEFINE_ENTITY_FIELD(blending, FIELD_INTEGER),
	DEFINE_ENTITY_FIELD(rendermode, FIELD_INTEGER),
	DEFINE_ENTITY_FIELD(renderamt, FIELD_FLOAT),
	DEFINE_ENTITY_FIELD(rendercolor, FIELD_VECTOR),
	DEFINE_ENTITY_FIELD(renderfx, FIELD_INTEGER),
	DEFINE_ENTITY_FIELD(health, FIELD_FLOAT),
	DEFINE_ENTITY_FIELD(frags, FIELD_FLOAT),
	DEFINE_ENTITY_FIELD(weapons, FIELD_INTEGER),
	DEFINE_ENTITY_FIELD(takedamage, FIELD_FLOAT),
	DEFINE_ENTITY_FIELD(deadflag, FIELD_FLOAT),
	DEFINE_ENTITY_FIELD(view_ofs, FIELD_VECTOR),
	DEFINE_ENTITY_FIELD(button, FIELD_INTEGER),
	DEFINE_ENTITY_FIELD(impulse, FIELD_INTEGER),
	DEFINE_ENTITY_FIELD(chain, FIELD_EDICT),
	DEFINE_ENTITY_FIELD(dmg_inflictor, FIELD_EDICT),
	DEFINE_ENTITY_FIELD(enemy, FIELD_EDICT),
	DEFINE_ENTITY_FIELD(aiment, FIELD_EDICT),
	DEFINE_ENTITY_FIELD(owner, FIELD_EDICT),
	DEFINE_ENTITY_FIELD(groundentity, FIELD_EDICT),
	DEFINE_ENTITY_FIELD(spawnflags, FIELD_INTEGER),
	DEFINE_ENTITY_FIELD(flags, FIELD_FLOAT),
	DEFINE_ENTITY_FIELD(colormap, FIELD_INTEGER),
	DEFINE_ENTITY_FIELD(team, FIELD_INTEGER),
	DEFINE_ENTITY_FIELD(max_health, FIELD_FLOAT),
	DEFINE_ENTITY_FIELD(teleport_time, FIELD_TIME),
	DEFINE_ENTITY_FIELD(armortype, FIELD_FLOAT),
	DEFINE_ENTITY_FIELD(armorvalue, FIELD_FLOAT),
	DEFINE_ENTITY_FIELD(waterlevel, FIELD_INTEGER),
	DEFINE_ENTITY_FIELD(watertype, FIELD_INTEGER),
	DEFINE_ENTITY_GLOBAL_FIELD(target, FIELD_STRING),
	DEFINE_ENTITY_GLOBAL_FIELD(targetname, FIELD_STRING),
	DEFINE_ENTITY_FIELD(netname, FIELD_STRING),
	DEFINE_ENTITY_FIELD(message, FIELD_STRING),
	DEFINE_ENTITY_FIELD(dmg_take, FIELD_FLOAT),
	DEFINE_ENTITY_FIELD(dmg_save, FIELD_FLOAT),
	DEFINE_ENTITY_FIELD(dmg, FIELD_FLOAT),
	DEFINE_ENTITY_FIELD(dmgtime, FIELD_TIME),
	DEFINE_ENTITY_FIELD(noise, FIELD_SOUNDNAME),
	DEFINE_ENTITY_FIELD(noise1, FIELD_SOUNDNAME),
	DEFINE_ENTITY_FIELD(noise2, FIELD_SOUNDNAME),
	DEFINE_ENTITY_FIELD(noise3, FIELD_SOUNDNAME),
	DEFINE_ENTITY_FIELD(speed, FIELD_FLOAT),
	DEFINE_ENTITY_FIELD(air_finished, FIELD_TIME),
	DEFINE_ENTITY_FIELD(pain_finished, FIELD_TIME),
	DEFINE_ENTITY_FIELD(radsuit_finished, FIELD_TIME),
};

#define ENTVARS_COUNT (sizeof(gEntvarsDescription) / sizeof(gEntvarsDescription[0]))

BOOL UTIL_GetNextBestWeapon(CBasePlayer *pPlayer, CBasePlayerItem *pCurrentWeapon)
{
	return g_pGameRules->GetNextBestWeapon(pPlayer, pCurrentWeapon);
}

float UTIL_AngleMod(float a)
{
	if (a < 0)
		a = a + 360 * ((int)(a / 360) + 1);
	else if (a >= 360)
		a = a - 360 * ((int)(a / 360));

	return a;
}

float UTIL_AngleDiff(float destAngle, float srcAngle)
{
	float delta = destAngle - srcAngle;

	if (destAngle > srcAngle)
	{
		if (delta >= 180)
			delta -= 360;
	}
	else
	{
		if (delta <= -180)
			delta += 360;
	}

	return delta;
}

Vector UTIL_VecToAngles(const Vector &vec)
{
	float rgflVecOut[3];
	VEC_TO_ANGLES(vec, rgflVecOut);
	return Vector(rgflVecOut);
}

void UTIL_MoveToOrigin(edict_t *pent, const Vector &vecGoal, float flDist, int iMoveType)
{
	float rgfl[3];
	vecGoal.CopyToArray(rgfl);
	MOVE_TO_ORIGIN(pent, rgfl, flDist, iMoveType);
}

int UTIL_EntitiesInBox(CBaseEntity **pList, int listMax, const Vector &mins, const Vector &maxs, int flagMask)
{
	edict_t *pEdict = g_engfuncs.pfnPEntityOfEntIndex(1);
	int count = 0;

	if (!pEdict)
		return count;

	for (int i = 1; i < gpGlobals->maxEntities; i++, pEdict++)
	{
		if (pEdict->free)
			continue;

		if (flagMask && !(pEdict->v.flags & flagMask))
			continue;

		if (mins.x > pEdict->v.absmax.x || mins.y > pEdict->v.absmax.y || mins.z > pEdict->v.absmax.z || maxs.x < pEdict->v.absmin.x || maxs.y < pEdict->v.absmin.y || maxs.z < pEdict->v.absmin.z)
			 continue;

		CBaseEntity *pEntity = CBaseEntity::Instance(pEdict);

		if (!pEntity)
			continue;

		pList[count++] = pEntity;

		if (count >= listMax)
			return count;
	}

	return count;
}

int UTIL_MonstersInSphere(CBaseEntity **pList, int listMax, const Vector &center, float radius)
{
	edict_t *pEdict = g_engfuncs.pfnPEntityOfEntIndex(1);
	int count = 0;
	float radiusSquared = radius * radius;

	if (!pEdict)
		return count;

	for (int i = 1; i < gpGlobals->maxEntities; i++, pEdict++)
	{
		if (pEdict->free)
			continue;

		if (!(pEdict->v.flags & (FL_CLIENT | FL_MONSTER)))
			continue;

		float delta = center.x - pEdict->v.origin.x;
		delta *= delta;

		if (delta > radiusSquared)
			continue;

		float distance = delta;
		delta = center.y - pEdict->v.origin.y;
		delta *= delta;
		distance += delta;

		if (distance > radiusSquared)
			continue;

		delta = center.z - (pEdict->v.absmin.z + pEdict->v.absmax.z) * 0.5;
		delta *= delta;
		distance += delta;

		if (distance > radiusSquared)
			continue;

		CBaseEntity *pEntity = CBaseEntity::Instance(pEdict);

		if (!pEntity)
			continue;

		pList[count++] = pEntity;

		if (count >= listMax)
			return count;
	}

	return count;
}

CBaseEntity *UTIL_FindEntityInSphere(CBaseEntity *pStartEntity, const Vector &vecCenter, float flRadius)
{
	edict_t *pentEntity = pStartEntity ? pStartEntity->edict() : NULL;
	pentEntity = FIND_ENTITY_IN_SPHERE(pentEntity, vecCenter, flRadius);

	if (!FNullEnt(pentEntity))
		return CBaseEntity::Instance(pentEntity);

	return NULL;
}

CBaseEntity *UTIL_FindEntityByString(CBaseEntity *pStartEntity, const char *szKeyword, const char *szValue)
{
	edict_t *pentEntity = pStartEntity ? pStartEntity->edict() : NULL;
	int startEntityIndex = ENTINDEX(pentEntity);

	if (*szKeyword != 'c')
	{
		pentEntity = FIND_ENTITY_BY_STRING(pentEntity, szKeyword, szValue);
	}
	else
	{
		int hash = 0;
		hash_item_t *item;
		int count;

		hash = CaseInsensitiveHash(szValue, stringsHashTable.Count());
		count = stringsHashTable.Count();
		item = &stringsHashTable[hash];

		while (item->pev)
		{
			if (!strcmp(STRING(item->pev->classname), szValue))
				break;

			hash = (hash + 1) % count;
			item = &stringsHashTable[hash];
		}

		if (!item->pev)
		{
			item->lastHash = NULL;
			return NULL;
		}

		if (pStartEntity)
		{
			if (item->lastHash && item->lastHash->pevIndex <= startEntityIndex)
				item = item->lastHash;

			while (item->pevIndex <= startEntityIndex)
			{
				if (!item->next)
					break;

				item = item->next;

				if (!(item->pevIndex <= startEntityIndex))
					break;
			}

			if (item->pevIndex == startEntityIndex)
			{
				stringsHashTable[hash].lastHash = NULL;
				return NULL;
			}
		}

		stringsHashTable[hash].lastHash = item;
		pentEntity = ENT(item->pev);
	}

	if (!FNullEnt(pentEntity))
		return CBaseEntity::Instance(pentEntity);

	return NULL;
}

CBaseEntity *UTIL_FindEntityByClassname(CBaseEntity *pStartEntity, const char *szName)
{
	return UTIL_FindEntityByString(pStartEntity, "classname", szName);
}

CBaseEntity *UTIL_FindEntityByTargetname(CBaseEntity *pStartEntity, const char *szName)
{
	return UTIL_FindEntityByString(pStartEntity, "targetname", szName);
}

CBaseEntity *UTIL_FindEntityGeneric(const char *szWhatever, Vector &vecSrc, float flRadius)
{
	CBaseEntity *pEntity = UTIL_FindEntityByTargetname(NULL, szWhatever);

	if (pEntity)
		return pEntity;

	CBaseEntity *pSearch = NULL;
	float flMaxDist2 = flRadius * flRadius;

	while ((pSearch = UTIL_FindEntityByClassname(pSearch, szWhatever)) != NULL)
	{
		float flDist2 = (pSearch->pev->origin - vecSrc).Length();
		flDist2 = flDist2 * flDist2;

		if (flMaxDist2 > flDist2)
		{
			pEntity = pSearch;
			flMaxDist2 = flDist2;
		}
	}

	return pEntity;
}

CBaseEntity *UTIL_PlayerByIndex(int playerIndex)
{
	CBaseEntity *pPlayer = NULL;

	if (playerIndex > 0 && playerIndex <= gpGlobals->maxClients)
	{
		edict_t *pPlayerEdict = INDEXENT(playerIndex);

		if (pPlayerEdict && !pPlayerEdict->free)
			pPlayer = CBaseEntity::Instance(pPlayerEdict);
	}

	return pPlayer;
}

void UTIL_MakeVectors(const Vector &vecAngles)
{
	MAKE_VECTORS(vecAngles);
}

void UTIL_MakeAimVectors(const Vector &vecAngles)
{
	float rgflVec[3];
	vecAngles.CopyToArray(rgflVec);
	rgflVec[0] = -rgflVec[0];
	MAKE_VECTORS(rgflVec);
}

#define SWAP(a, b, temp) ((temp)=(a),(a)=(b),(b)=(temp))

void UTIL_MakeInvVectors(const Vector &vec, globalvars_t *pgv)
{
	MAKE_VECTORS(vec);

	float tmp;
	pgv->v_right = pgv->v_right * -1;

	SWAP(pgv->v_forward.y, pgv->v_right.x, tmp);
	SWAP(pgv->v_forward.z, pgv->v_up.x, tmp);
	SWAP(pgv->v_right.z, pgv->v_up.y, tmp);
}

void UTIL_EmitAmbientSound(edict_t *entity, const Vector &vecOrigin, const char *samp, float vol, float attenuation, int fFlags, int pitch)
{
	float rgfl[3];
	vecOrigin.CopyToArray(rgfl);

	if (samp && *samp == '!')
	{
		char name[32];

		if (SENTENCEG_Lookup(samp, name) >= 0)
			EMIT_AMBIENT_SOUND(entity, rgfl, name, vol, attenuation, fFlags, pitch);
	}
	else
		EMIT_AMBIENT_SOUND(entity, rgfl, samp, vol, attenuation, fFlags, pitch);
}

static unsigned short FixedUnsigned16(float value, float scale)
{
	int output = (int)(value * scale);

	if (output < 0)
		output = 0;

	if (output > 0xFFFF)
		output = 0xFFFF;

	return (unsigned short)output;
}

static short FixedSigned16(float value, float scale)
{
	int output = (int)(value * scale);

	if (output > 32767)
		output = 32767;

	if (output < -32768)
		output = -32768;

	return (short)output;
}

void UTIL_SingleScreenShake(float amplitude, float frequency, float duration, CBaseEntity *pPlayer)
{
	ScreenShake shake;
	shake.duration = FixedUnsigned16(duration, 1 << 12);
	shake.frequency = FixedUnsigned16(frequency, 1 << 8);
	shake.amplitude = FixedUnsigned16(amplitude, 1 << 12);

	MESSAGE_BEGIN(MSG_ONE, gmsgShake, NULL, pPlayer->edict());
	WRITE_SHORT(shake.amplitude);
	WRITE_SHORT(shake.duration);
	WRITE_SHORT(shake.frequency);
	MESSAGE_END();
}

void UTIL_ScreenShake(const Vector &center, float amplitude, float frequency, float duration, float radius)
{
	ScreenShake shake;
	shake.duration = FixedUnsigned16(duration, 1 << 12);
	shake.frequency = FixedUnsigned16(frequency, 1 << 8);

	for (int i = 1; i <= gpGlobals->maxClients; i++)
	{
		CBaseEntity *pPlayer = UTIL_PlayerByIndex(i);

		if (!pPlayer || !(pPlayer->pev->flags & FL_ONGROUND))
			continue;

		float localAmplitude = 0;

		if (radius > 0)
		{
			Vector delta = center - pPlayer->pev->origin;
			float distance = delta.Length();

			if (distance < radius)
				localAmplitude = amplitude;
		}
		else
			localAmplitude = amplitude;

		if (localAmplitude)
		{
			shake.amplitude = FixedUnsigned16(localAmplitude, 1 << 12);

			MESSAGE_BEGIN(MSG_ONE, gmsgShake, NULL, pPlayer->edict());
			WRITE_SHORT(shake.amplitude);
			WRITE_SHORT(shake.duration);
			WRITE_SHORT(shake.frequency);
			MESSAGE_END();
		}
	}
}

void UTIL_ScreenShakeAll(const Vector &center, float amplitude, float frequency, float duration)
{
	UTIL_ScreenShake(center, amplitude, frequency, duration, 0);
}

void UTIL_ScreenFadeBuild(ScreenFade &fade, const Vector &color, float fadeTime, float fadeHold, int alpha, int flags)
{
	fade.duration = FixedUnsigned16(fadeTime, 1 << 12);
	fade.holdTime = FixedUnsigned16(fadeHold, 1 << 12);
	fade.r = (int)color.x;
	fade.g = (int)color.y;
	fade.b = (int)color.z;
	fade.a = alpha;
	fade.fadeFlags = flags;
}

void UTIL_ScreenFadeWrite(const ScreenFade &fade, CBaseEntity *pEntity)
{
	if (!pEntity || !pEntity->IsNetClient())
		return;

	MESSAGE_BEGIN(MSG_ONE, gmsgFade, NULL, pEntity->edict());
	WRITE_SHORT(fade.duration);
	WRITE_SHORT(fade.holdTime);
	WRITE_SHORT(fade.fadeFlags);
	WRITE_BYTE(fade.r);
	WRITE_BYTE(fade.g);
	WRITE_BYTE(fade.b);
	WRITE_BYTE(fade.a);
	MESSAGE_END();
}

void UTIL_ScreenFadeAll(const Vector &color, float fadeTime, float fadeHold, int alpha, int flags)
{
	ScreenFade fade;
	UTIL_ScreenFadeBuild(fade, color, fadeTime, fadeHold, alpha, flags);

	for (int i = 1; i <= gpGlobals->maxClients; i++)
	{
		CBaseEntity *pPlayer = UTIL_PlayerByIndex(i);
		UTIL_ScreenFadeWrite(fade, pPlayer);
	}
}

void UTIL_ScreenFade(CBaseEntity *pEntity, const Vector &color, float fadeTime, float fadeHold, int alpha, int flags)
{
	ScreenFade fade;
	UTIL_ScreenFadeBuild(fade, color, fadeTime, fadeHold, alpha, flags);
	UTIL_ScreenFadeWrite(fade, pEntity);
}

void UTIL_HudMessage(CBaseEntity *pEntity, const hudtextparms_t &textparms, const char *pMessage)
{
	if (!pEntity || !pEntity->IsNetClient())
		return;

	MESSAGE_BEGIN(MSG_ONE, SVC_TEMPENTITY, NULL, pEntity->edict());
	WRITE_BYTE(TE_TEXTMESSAGE);
	WRITE_BYTE(textparms.channel & 0xFF);
	WRITE_SHORT(FixedSigned16(textparms.x, 1 << 13));
	WRITE_SHORT(FixedSigned16(textparms.y, 1 << 13));
	WRITE_BYTE(textparms.effect);
	WRITE_BYTE(textparms.r1);
	WRITE_BYTE(textparms.g1);
	WRITE_BYTE(textparms.b1);
	WRITE_BYTE(textparms.a1);
	WRITE_BYTE(textparms.r2);
	WRITE_BYTE(textparms.g2);
	WRITE_BYTE(textparms.b2);
	WRITE_BYTE(textparms.a2);
	WRITE_SHORT(FixedUnsigned16(textparms.fadeinTime, 1 << 8));
	WRITE_SHORT(FixedUnsigned16(textparms.fadeoutTime, 1 << 8));
	WRITE_SHORT(FixedUnsigned16(textparms.holdTime, 1 << 8));

	if (textparms.effect == 2)
		WRITE_SHORT(FixedUnsigned16(textparms.fxTime, 1 << 8));

	if (strlen(pMessage) >= 512)
	{
		char tmp[512];
		strncpy(tmp, pMessage, 511);
		tmp[511] = '\0';
		WRITE_STRING(tmp);
	}
	else
		WRITE_STRING(pMessage);

	MESSAGE_END();
}

void UTIL_HudMessageAll(const hudtextparms_t &textparms, const char *pMessage)
{
	for (int i = 1; i <= gpGlobals->maxClients; i++)
	{
		CBaseEntity *pPlayer = UTIL_PlayerByIndex(i);

		if (pPlayer)
			UTIL_HudMessage(pPlayer, textparms, pMessage);
	}
}

extern int gmsgTextMsg, gmsgSayText;

void UTIL_ClientPrintAll(int msg_dest, const char *msg_name, const char *param1, const char *param2, const char *param3, const char *param4)
{
	MESSAGE_BEGIN(MSG_ALL, gmsgTextMsg);
	WRITE_BYTE(msg_dest);
	WRITE_STRING(msg_name);

	if (param1)
		WRITE_STRING(param1);

	if (param2)
		WRITE_STRING(param2);

	if (param3)
		WRITE_STRING(param3);

	if (param4)
		WRITE_STRING(param4);

	MESSAGE_END();
}

void ClientPrint(entvars_t *client, int msg_dest, const char *msg_name, const char *param1, const char *param2, const char *param3, const char *param4)
{
	MESSAGE_BEGIN(MSG_ONE, gmsgTextMsg, NULL, client);
	WRITE_BYTE(msg_dest);
	WRITE_STRING(msg_name);

	if (param1)
		WRITE_STRING(param1);

	if (param2)
		WRITE_STRING(param2);

	if (param3)
		WRITE_STRING(param3);

	if (param4)
		WRITE_STRING(param4);

	MESSAGE_END();
}

void UTIL_PlayMP3(CBasePlayer *pPlayer, const char *szMP3)
{
	char szCommand[256];
	sprintf(szCommand, "mp3 play %s\n", szMP3);

	if(pPlayer == NULL)
	{
		CBaseEntity *pEntity = NULL;
		while (pEntity = UTIL_FindEntityByClassname(pEntity, "player"))
		{
			if (FNullEnt(pEntity->edict()))
				break;
			if (!pEntity->IsPlayer())
				continue;
			if (pEntity->pev->flags == FL_DORMANT)
				continue;

			CLIENT_COMMAND(pEntity->edict(), szCommand);
		}
	}
	else
	{
		CLIENT_COMMAND(pPlayer->edict(), szCommand);
	}
}

void UTIL_PlayTeamMP3(int iTeam, const char *szMP3)
{
	char szCommand[256];
	sprintf(szCommand, "mp3 play %s\n", szMP3);

	CBaseEntity *pEntity = NULL;
	while (pEntity = UTIL_FindEntityByClassname(pEntity, "player"))
	{
		if (FNullEnt(pEntity->edict()))
			break;
		if (!pEntity->IsPlayer())
			continue;
		if (pEntity->pev->flags == FL_DORMANT)
			continue;
		CBasePlayer *pPlayer = GetClassPtr((CBasePlayer *)pEntity->pev);
		if(iTeam == pPlayer->m_iTeam)
		{
			CLIENT_COMMAND(pEntity->edict(), szCommand);
		}
	}
}

void UTIL_PlayWAV(CBasePlayer *pPlayer, const char *szWAV)
{
	char szCommand[256];
	sprintf(szCommand, "spk \"%s\"\n", szWAV);
	if(pPlayer == NULL)
	{
		CBaseEntity *pEntity = NULL;
		while (pEntity = UTIL_FindEntityByClassname(pEntity, "player"))
		{
			if (FNullEnt(pEntity->edict()))
				break;
			if (!pEntity->IsPlayer())
				continue;
			if (pEntity->pev->flags == FL_DORMANT)
				continue;

			CLIENT_COMMAND(pEntity->edict(), szCommand);
		}
	}
	else
	{
		CLIENT_COMMAND(pPlayer->edict(), szCommand);
	}
}

void UTIL_PlayTeamWAV(int iTeam, const char *szWAV)
{
	char szCommand[256];
	sprintf(szCommand, "spk \"%s\"\n", szWAV);
	CBaseEntity *pEntity = NULL;
	while (pEntity = UTIL_FindEntityByClassname(pEntity, "player"))
	{
		if (FNullEnt(pEntity->edict()))
			break;
		if (!pEntity->IsPlayer())
			continue;
		if (pEntity->pev->flags == FL_DORMANT)
			continue;
		CBasePlayer *pPlayer = GetClassPtr((CBasePlayer *)pEntity->pev);
		if(iTeam == pPlayer->m_iTeam)
		{
			CLIENT_COMMAND(pEntity->edict(), szCommand);
		}
	}
}

void UTIL_SayText(const char *pText, CBaseEntity *pEntity)
{
	if (!pEntity->IsNetClient())
		return;

	MESSAGE_BEGIN(MSG_ONE, gmsgSayText, NULL, pEntity->edict());
	WRITE_BYTE(pEntity->entindex());
	WRITE_STRING(pText);
	MESSAGE_END();
}

void UTIL_SayTextAll(const char *pText, CBaseEntity *pEntity)
{
	MESSAGE_BEGIN(MSG_ALL, gmsgSayText, NULL);
	WRITE_BYTE(pEntity->entindex());
	WRITE_STRING(pText);
	MESSAGE_END();
}

char *UTIL_dtos1(int d)
{
	static char buf[8];
	sprintf(buf, "%d", d);
	return buf;
}

char *UTIL_dtos2(int d)
{
	static char buf[8];
	sprintf(buf, "%d", d);
	return buf;
}

char *UTIL_dtos3(int d)
{
	static char buf[8];
	sprintf(buf, "%d", d);
	return buf;
}

char *UTIL_dtos4(int d)
{
	static char buf[8];
	sprintf(buf, "%d", d);
	return buf;
}

void UTIL_ShowMessage(const char *pString, CBaseEntity *pEntity)
{
	if (!pEntity || !pEntity->IsNetClient())
		return;

	MESSAGE_BEGIN(MSG_ONE, gmsgHudText, NULL, pEntity->edict());
	WRITE_STRING(pString);
	MESSAGE_END();
}

void UTIL_ShowMessageAll(const char *pString)
{
	for (int i = 1; i <= gpGlobals->maxClients; i++)
	{
		CBaseEntity *pPlayer = UTIL_PlayerByIndex(i);

		if (pPlayer)
			UTIL_ShowMessage(pString, pPlayer);
	}
}

void UTIL_TraceLine(const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, IGNORE_GLASS ignoreGlass, edict_t *pentIgnore, TraceResult *ptr)
{
	g_fIsTraceLine = 1;
	TRACE_LINE(vecStart, vecEnd, (igmon == ignore_monsters ? TRUE : FALSE) | (ignoreGlass ? 0x100 : 0), pentIgnore, ptr);
	g_fIsTraceLine = 0;
}

void UTIL_TraceLine(const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, edict_t *pentIgnore, TraceResult *ptr)
{
	g_fIsTraceLine = 1;
	TRACE_LINE(vecStart, vecEnd, (igmon == ignore_monsters ? TRUE : FALSE), pentIgnore, ptr);
	g_fIsTraceLine = 0;
}

void UTIL_TraceHull(const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, int hullNumber, edict_t *pentIgnore, TraceResult *ptr)
{
	g_fIsTraceLine = 1;
	TRACE_HULL(vecStart, vecEnd, (igmon == ignore_monsters ? TRUE : FALSE), hullNumber, pentIgnore, ptr);
	g_fIsTraceLine = 0;
}

void UTIL_TraceModel(const Vector &vecStart, const Vector &vecEnd, int hullNumber, edict_t *pentModel, TraceResult *ptr)
{
	g_fIsTraceLine = 1;
	g_engfuncs.pfnTraceModel(vecStart, vecEnd, hullNumber, pentModel, ptr);
	g_fIsTraceLine = 0;
}

TraceResult UTIL_GetGlobalTrace(void)
{
	TraceResult tr;

	tr.fAllSolid = (int)gpGlobals->trace_allsolid;
	tr.fStartSolid = (int)gpGlobals->trace_startsolid;
	tr.fInOpen = (int)gpGlobals->trace_inopen;
	tr.fInWater = (int)gpGlobals->trace_inwater;
	tr.flFraction = gpGlobals->trace_fraction;
	tr.flPlaneDist = gpGlobals->trace_plane_dist;
	tr.pHit = gpGlobals->trace_ent;
	tr.vecEndPos = gpGlobals->trace_endpos;
	tr.vecPlaneNormal = gpGlobals->trace_plane_normal;
	tr.iHitgroup = gpGlobals->trace_hitgroup;
	return tr;
}

void UTIL_SetSize(entvars_t *pev, const Vector &vecMin, const Vector &vecMax)
{
	SET_SIZE(ENT(pev), vecMin, vecMax);
}

float UTIL_VecToYaw(const Vector &vec)
{
	return VEC_TO_YAW(vec);
}

void UTIL_SetOrigin(entvars_t *pev, const Vector &vecOrigin)
{
	SET_ORIGIN(ENT(pev), vecOrigin);
}

void UTIL_ParticleEffect(const Vector &vecOrigin, const Vector &vecDirection, ULONG ulColor, ULONG ulCount)
{
	PARTICLE_EFFECT(vecOrigin, vecDirection, (float)ulColor, (float)ulCount);
}

float UTIL_Approach(float target, float value, float speed)
{
	float delta = target - value;

	if (delta > speed)
		value += speed;
	else if (delta < -speed)
		value -= speed;
	else
		value = target;

	return value;
}

float UTIL_ApproachAngle(float target, float value, float speed)
{
	target = UTIL_AngleMod(target);
	value = UTIL_AngleMod(target);

	float delta = target - value;

	if (speed < 0)
		speed = -speed;

	if (delta < -180)
		delta += 360;
	else if (delta > 180)
		delta -= 360;

	if (delta > speed)
		value += speed;
	else if (delta < -speed)
		value -= speed;
	else
		value = target;

	return value;
}

float UTIL_AngleDistance(float next, float cur)
{
	float delta = next - cur;

	if (delta < -180)
		delta += 360;
	else if (delta > 180)
		delta -= 360;

	return delta;
}

float UTIL_SplineFraction(float value, float scale)
{
	value = scale * value;
	float valueSquared = value * value;
	return 3 * valueSquared - 2 * valueSquared * value;
}

char *UTIL_VarArgs(char *format, ...)
{
	va_list argptr;
	static char string[1024];

	va_start(argptr, format);
	vsprintf(string, format, argptr);
	va_end(argptr);

	return string;
}

Vector UTIL_GetAimVector(edict_t *pent, float flSpeed)
{
	Vector tmp;
	GET_AIM_VECTOR(pent, flSpeed, tmp);
	return tmp;
}

int UTIL_IsMasterTriggered(string_t sMaster, CBaseEntity *pActivator)
{
	if (sMaster)
	{
		edict_t *pentTarget = FIND_ENTITY_BY_TARGETNAME(NULL, STRING(sMaster));

		if (!FNullEnt(pentTarget))
		{
			CBaseEntity *pMaster = CBaseEntity::Instance(pentTarget);

			if (pMaster && (pMaster->ObjectCaps() & FCAP_MASTER))
				return pMaster->IsTriggered(pActivator);
		}

		ALERT(at_console, "Master was null or not a master!\n");
	}

	return 1;
}

BOOL UTIL_ShouldShowBlood(int color)
{
	if (color != DONT_BLEED)
	{
		if (color == BLOOD_COLOR_RED)
		{
			if (CVAR_GET_FLOAT("violence_hblood") != 0)
				return TRUE;
		}
		else
		{
			if (CVAR_GET_FLOAT("violence_ablood") != 0)
				return TRUE;
		}
	}

	return FALSE;
}

int UTIL_PointContents(const Vector &vec)
{
	return POINT_CONTENTS(vec);
}

void UTIL_BloodStream(const Vector &origin, const Vector &direction, int color, int amount)
{
	if (!UTIL_ShouldShowBlood(color))
		return;

	if (g_Language == LANGUAGE_GERMAN && color == BLOOD_COLOR_RED)
		color = 0;

	MESSAGE_BEGIN(MSG_PVS, SVC_TEMPENTITY, origin);
	WRITE_BYTE(TE_BLOODSTREAM);
	WRITE_COORD(origin.x);
	WRITE_COORD(origin.y);
	WRITE_COORD(origin.z);
	WRITE_COORD(direction.x);
	WRITE_COORD(direction.y);
	WRITE_COORD(direction.z);
	WRITE_BYTE(color);
	WRITE_BYTE(min(amount, 255));
	MESSAGE_END();
}

void UTIL_BloodDrips(const Vector &origin, const Vector &direction, int color, int amount)
{
	if (!UTIL_ShouldShowBlood(color))
		return;

	if (color == DONT_BLEED || !amount)
		return;

	if (g_Language == LANGUAGE_GERMAN && color == BLOOD_COLOR_RED)
		color = 0;

	if (g_pGameRules->IsMultiplayer())
		amount *= 2;

	if (amount > 255)
		amount = 255;

	MESSAGE_BEGIN(MSG_PVS, SVC_TEMPENTITY, origin);
	WRITE_BYTE(TE_BLOODSPRITE);
	WRITE_COORD(origin.x);
	WRITE_COORD(origin.y);
	WRITE_COORD(origin.z);
	WRITE_SHORT(g_sModelIndexBloodSpray);
	WRITE_SHORT(g_sModelIndexBloodDrop);
	WRITE_BYTE(color);
	WRITE_BYTE(min(max(3, amount / 10), 16));
	MESSAGE_END();
}

Vector UTIL_RandomBloodVector(void)
{
	Vector direction;

	direction.x = RANDOM_FLOAT(-1, 1);
	direction.y = RANDOM_FLOAT(-1, 1);
	direction.z = RANDOM_FLOAT(0, 1);

	return direction;
}

void UTIL_BloodDecalTrace(TraceResult *pTrace, int bloodColor)
{
	if (UTIL_ShouldShowBlood(bloodColor))
	{
		if (bloodColor == BLOOD_COLOR_RED)
			UTIL_DecalTrace(pTrace, DECAL_BLOOD1 + RANDOM_LONG(0, 5));
		else
			UTIL_DecalTrace(pTrace, DECAL_YBLOOD1 + RANDOM_LONG(0, 5));
	}
}

void UTIL_DecalTrace(TraceResult *pTrace, int decalNumber)
{
	if (decalNumber < 0)
		return;

	int index = gDecals[decalNumber].index;

	if (index < 0)
		return;

	if (pTrace->flFraction == 1)
		return;

	short entityIndex;

	if (pTrace->pHit)
	{
		CBaseEntity *pEntity = CBaseEntity::Instance(pTrace->pHit);

		if (pEntity && !pEntity->IsBSPModel())
			return;

		entityIndex = ENTINDEX(pTrace->pHit);
	}
	else
		entityIndex = 0;

	int message = TE_DECAL;

	if (entityIndex != 0)
	{
		if (index > 255)
		{
			message = TE_DECALHIGH;
			index -= 256;
		}
	}
	else
	{
		message = TE_WORLDDECAL;

		if (index > 255)
		{
			message = TE_WORLDDECALHIGH;
			index -= 256;
		}
	}

	MESSAGE_BEGIN(MSG_BROADCAST, SVC_TEMPENTITY);
	WRITE_BYTE(message);
	WRITE_COORD(pTrace->vecEndPos.x);
	WRITE_COORD(pTrace->vecEndPos.y);
	WRITE_COORD(pTrace->vecEndPos.z);
	WRITE_BYTE(index);

	if (entityIndex)
		WRITE_SHORT(entityIndex);

	MESSAGE_END();
}

void UTIL_PlayerDecalTrace(TraceResult *pTrace, int playernum, int decalNumber, BOOL bIsCustom)
{
	int index;

	if (!bIsCustom)
	{
		if (decalNumber < 0)
			return;

		index = gDecals[decalNumber].index;

		if (index < 0)
			return;
	}
	else
		index = decalNumber;

	if (pTrace->flFraction == 1)
		return;

	MESSAGE_BEGIN(MSG_BROADCAST, SVC_TEMPENTITY);
	WRITE_BYTE(TE_PLAYERDECAL);
	WRITE_BYTE(playernum);
	WRITE_COORD(pTrace->vecEndPos.x);
	WRITE_COORD(pTrace->vecEndPos.y);
	WRITE_COORD(pTrace->vecEndPos.z);
	WRITE_SHORT((short)ENTINDEX(pTrace->pHit));
	WRITE_BYTE(index);
	MESSAGE_END();
}

void UTIL_GunshotDecalTrace(TraceResult *pTrace, int decalNumber)
{
	if (decalNumber < 0)
		return;

	int index = gDecals[decalNumber].index;

	if (index < 0)
		return;

	if (pTrace->flFraction == 1)
		return;

	MESSAGE_BEGIN(MSG_PAS, SVC_TEMPENTITY, pTrace->vecEndPos);
	WRITE_BYTE(TE_GUNSHOTDECAL);
	WRITE_COORD(pTrace->vecEndPos.x);
	WRITE_COORD(pTrace->vecEndPos.y);
	WRITE_COORD(pTrace->vecEndPos.z);
	WRITE_SHORT((short)ENTINDEX(pTrace->pHit));
	WRITE_BYTE(index);
	MESSAGE_END();
}

void UTIL_Sparks(const Vector &position)
{
	MESSAGE_BEGIN(MSG_PVS, SVC_TEMPENTITY, position);
	WRITE_BYTE(TE_SPARKS);
	WRITE_COORD(position.x);
	WRITE_COORD(position.y);
	WRITE_COORD(position.z);
	MESSAGE_END();
}

void UTIL_Ricochet(const Vector &position, float scale)
{
	MESSAGE_BEGIN(MSG_PVS, SVC_TEMPENTITY, position);
	WRITE_BYTE(TE_ARMOR_RICOCHET);
	WRITE_COORD(position.x);
	WRITE_COORD(position.y);
	WRITE_COORD(position.z);
	WRITE_BYTE((int)(scale * 10));
	MESSAGE_END();
}

BOOL UTIL_TeamsMatch(const char *pTeamName1, const char *pTeamName2)
{
	if (!g_pGameRules->IsTeamplay())
		return TRUE;

	if (*pTeamName1 != '\0' && *pTeamName2 != '\0')
	{
		if (!stricmp(pTeamName1, pTeamName2))
			return TRUE;
	}

	return FALSE;
}

void UTIL_StringToVector(float *pVector, const char *pString)
{
	char *pstr, *pfront, tempString[128];
	strcpy(tempString, pString);
	pstr = pfront = tempString;
	int j = 0;

	for (; j < 3; j++)
	{
		pVector[j] = atof(pfront);

		while (*pstr && *pstr != ' ')
			pstr++;

		if (!*pstr)
			break;

		pstr++;
		pfront = pstr;
	}

	if (j < 2)
	{
		for (j = j + 1;j < 3; j++)
			pVector[j] = 0;
	}
}

void UTIL_StringToIntArray(int *pVector, int count, const char *pString)
{
	char *pstr, *pfront, tempString[128];
	strcpy(tempString, pString);
	pstr = pfront = tempString;
	int j = 0;

	for (j = 0; j < count; j++)
	{
		pVector[j] = atoi(pfront);

		while (*pstr && *pstr != ' ')
			pstr++;

		if (!*pstr)
			break;

		pstr++;
		pfront = pstr;
	}

	for (j++; j < count; j++)
		pVector[j] = 0;
}

Vector UTIL_ClampVectorToBox(const Vector &input, const Vector &clampSize)
{
	Vector sourceVector = input;

	if (sourceVector.x > clampSize.x)
		sourceVector.x -= clampSize.x;
	else if (sourceVector.x < -clampSize.x)
		sourceVector.x += clampSize.x;
	else
		sourceVector.x = 0;

	if (sourceVector.y > clampSize.y)
		sourceVector.y -= clampSize.y;
	else if (sourceVector.y < -clampSize.y)
		sourceVector.y += clampSize.y;
	else
		sourceVector.y = 0;

	if (sourceVector.z > clampSize.z)
		sourceVector.z -= clampSize.z;
	else if (sourceVector.z < -clampSize.z)
		sourceVector.z += clampSize.z;
	else
		sourceVector.z = 0;

	return sourceVector.Normalize();
}

float UTIL_WaterLevel(const Vector &position, float minz, float maxz)
{
	Vector midUp = position;
	midUp.z = minz;

	if (UTIL_PointContents(midUp) != CONTENTS_WATER)
		return minz;

	midUp.z = maxz;

	if (UTIL_PointContents(midUp) == CONTENTS_WATER)
		return maxz;

	float diff = maxz - minz;

	while (diff > 1)
	{
		midUp.z = minz + diff / 2;

		if (UTIL_PointContents(midUp) == CONTENTS_WATER)
			minz = midUp.z;
		else
			maxz = midUp.z;

		diff = maxz - minz;
	}

	return midUp.z;
}

extern DLL_GLOBAL short g_sModelIndexBubbles;

void UTIL_Bubbles(Vector mins, Vector maxs, int count)
{
	Vector mid = (mins + maxs) * 0.5;
	float flHeight = UTIL_WaterLevel(mid, mid.z, mid.z + 1024);
	flHeight = flHeight - mins.z;

	MESSAGE_BEGIN(MSG_PAS, SVC_TEMPENTITY, mid);
	WRITE_BYTE(TE_BUBBLES);
	WRITE_COORD(mins.x);
	WRITE_COORD(mins.y);
	WRITE_COORD(mins.z);
	WRITE_COORD(maxs.x);
	WRITE_COORD(maxs.y);
	WRITE_COORD(maxs.z);
	WRITE_COORD(flHeight);
	WRITE_SHORT(g_sModelIndexBubbles);
	WRITE_BYTE(count);
	WRITE_COORD(8);
	MESSAGE_END();
}

void UTIL_BubbleTrail(Vector from, Vector to, int count)
{
	float flHeight = UTIL_WaterLevel(from, from.z, from.z + 256) - from.z;

	if (flHeight < 8)
	{
		flHeight = UTIL_WaterLevel(to, to.z, to.z + 256) - to.z;

		if (flHeight < 8)
			return;

		flHeight = flHeight + to.z - from.z;
	}

	if (count > 255)
		count = 255;

	MESSAGE_BEGIN(MSG_BROADCAST, SVC_TEMPENTITY);
	WRITE_BYTE(TE_BUBBLETRAIL);
	WRITE_COORD(from.x);
	WRITE_COORD(from.y);
	WRITE_COORD(from.z);
	WRITE_COORD(to.x);
	WRITE_COORD(to.y);
	WRITE_COORD(to.z);
	WRITE_COORD(flHeight);
	WRITE_SHORT(g_sModelIndexBubbles);
	WRITE_BYTE(count);
	WRITE_COORD(8);
	MESSAGE_END();
}

void UTIL_Remove(CBaseEntity *pEntity)
{
	if (!pEntity)
		return;

	pEntity->UpdateOnRemove();
	pEntity->pev->flags |= FL_KILLME;
	pEntity->pev->targetname = 0;
}

BOOL UTIL_IsValidEntity(edict_t *pent)
{
	if (!pent || pent->free || (pent->v.flags & FL_KILLME))
		return FALSE;

	return TRUE;
}

void UTIL_PrecacheOther(const char *szClassname)
{
	edict_t *pent = CREATE_NAMED_ENTITY(MAKE_STRING(szClassname));

	if (FNullEnt(pent))
	{
		ALERT(at_console, "NULL Ent in UTIL_PrecacheOther\n");
		return;
	}

	CBaseEntity *pEntity = CBaseEntity::Instance(VARS(pent));

	if (pEntity)
		pEntity->Precache();

	REMOVE_ENTITY(pent);
}

void UTIL_LogPrintf(char *fmt, ...)
{
	va_list argptr;
	static char string[1024];

	va_start(argptr, fmt);
	vsprintf(string, fmt, argptr);
	va_end(argptr);

	ALERT(at_logged, "%s", string);
}

float UTIL_DotPoints(const Vector &vecSrc, const Vector &vecCheck, const Vector &vecDir)
{
	Vector2D vec2LOS = (vecCheck - vecSrc).Make2D();
	vec2LOS = vec2LOS.Normalize();
	return DotProduct(vec2LOS, (vecDir.Make2D()));
}

void UTIL_StripToken(const char *pKey, char *pDest)
{
	int i = 0;

	while (pKey[i] && pKey[i] != '#')
	{
		pDest[i] = pKey[i];
		i++;
	}

	pDest[i] = 0;
}
//sohl start
char* GetStringForUseType( USE_TYPE useType )
{
	switch(useType)
	{
	case USE_ON: return "USE_ON";
	case USE_OFF: return "USE_OFF";
	case USE_TOGGLE: return "USE_TOGGLE";
	case USE_KILL: return "USE_KILL";
	case USE_NOT: return "USE_NOT";
	default:
		return "USE_UNKNOWN!?";
	}
}

char* GetStringForState( STATE state )
{
	switch(state)
	{
	case STATE_ON: return "ON";
	case STATE_OFF: return "OFF";
	case STATE_TURN_ON: return "TURN ON";
	case STATE_TURN_OFF: return "TURN OFF";
	case STATE_IN_USE: return "IN USE";
	default:
		return "STATE_UNKNOWN!?";
	}
}
//sohl end

static int gSizes[FIELD_TYPECOUNT] =
{
	sizeof(float),		// FIELD_FLOAT
	sizeof(int),		// FIELD_STRING
	sizeof(int),		// FIELD_ENTITY
	sizeof(int),		// FIELD_CLASSPTR
	sizeof(int),		// FIELD_EHANDLE
	sizeof(int),		// FIELD_entvars_t
	sizeof(int),		// FIELD_EDICT
	sizeof(float) * 3,	// FIELD_VECTOR
	sizeof(float) * 3,	// FIELD_POSITION_VECTOR
	sizeof(int *),		// FIELD_POINTER
	sizeof(int),		// FIELD_INTEGER
	sizeof(int *),		// FIELD_FUNCTION
	sizeof(int),		// FIELD_BOOLEAN
	sizeof(short),		// FIELD_SHORT
	sizeof(char),		// FIELD_CHARACTER
	sizeof(float),		// FIELD_TIME
	sizeof(int),		// FIELD_MODELNAME
	sizeof(int),		// FIELD_SOUNDNAME
};

CSaveRestoreBuffer::CSaveRestoreBuffer(void)
{
	m_pdata = NULL;
}

CSaveRestoreBuffer::CSaveRestoreBuffer(SAVERESTOREDATA *pdata)
{
	m_pdata = pdata;
}

CSaveRestoreBuffer::~CSaveRestoreBuffer(void)
{
}

int CSaveRestoreBuffer::EntityIndex(CBaseEntity *pEntity)
{
	if (!pEntity)
		return -1;

	return EntityIndex(pEntity->pev);
}

int CSaveRestoreBuffer::EntityIndex(entvars_t *pevLookup)
{
	if (!pevLookup)
		return -1;

	return EntityIndex(ENT(pevLookup));
}

int CSaveRestoreBuffer::EntityIndex(EOFFSET eoLookup)
{
	return EntityIndex(ENT(eoLookup));
}

int CSaveRestoreBuffer::EntityIndex(edict_t *pentLookup)
{
	if (!m_pdata || !pentLookup)
		return -1;

	for (int i = 0; i < m_pdata->tableCount; i++)
	{
		ENTITYTABLE *pTable = m_pdata->pTable + i;

		if (pTable->pent == pentLookup)
			return i;
	}

	return -1;
}

edict_t *CSaveRestoreBuffer::EntityFromIndex(int entityIndex)
{
	if (!m_pdata || entityIndex < 0)
		return NULL;

	for (int i = 0; i < m_pdata->tableCount; i++)
	{
		ENTITYTABLE *pTable = m_pdata->pTable + i;

		if (pTable->id == entityIndex)
			return pTable->pent;
	}

	return NULL;
}

int CSaveRestoreBuffer::EntityFlagsSet(int entityIndex, int flags)
{
	if (!m_pdata || entityIndex < 0)
		return 0;

	if (entityIndex > m_pdata->tableCount)
		return 0;

	m_pdata->pTable[entityIndex].flags |= flags;
	return m_pdata->pTable[entityIndex].flags;
}

void CSaveRestoreBuffer::BufferRewind(int size)
{
	if (!m_pdata)
		return;

	if (m_pdata->size < size)
		size = m_pdata->size;

	m_pdata->pCurrentData -= size;
	m_pdata->size -= size;
}

#ifndef _WIN32
extern "C"
{
unsigned _rotr(unsigned val, int shift)
{
	register unsigned lobit;
	register unsigned num = val;

	shift &= 0x1F;

	while (shift--)
	{
		lobit = num & 1;
		num >>= 1;

		if (lobit)
			num |= 0x80000000;
	}

	return num;
}
}
#endif

unsigned int CSaveRestoreBuffer::HashString(const char *pszToken)
{
	unsigned int hash = 0;

	while (*pszToken)
		hash = _rotr(hash, 4) ^ *pszToken++;

	return hash;
}

unsigned short CSaveRestoreBuffer::TokenHash(const char *pszToken)
{
	unsigned short hash = (unsigned short)(HashString(pszToken) % (unsigned)m_pdata->tokenCount);

	for (int i = 0; i < m_pdata->tokenCount; i++)
	{
		int index = hash + i;

		if (index >= m_pdata->tokenCount)
			index -= m_pdata->tokenCount;

		if (!m_pdata->pTokens[index] || !strcmp(pszToken, m_pdata->pTokens[index]))
		{
			m_pdata->pTokens[index] = (char *)pszToken;
			return index;
		}
	}

	ALERT(at_error, "CSaveRestoreBuffer::TokenHash() is COMPLETELY FULL!");
	return 0;
}

void CSave::WriteData(const char *pname, int size, const char *pdata)
{
	BufferField(pname, size, pdata);
}

void CSave::WriteShort(const char *pname, const short *data, int count)
{
	BufferField(pname, sizeof(short) * count, (const char *)data);
}

void CSave::WriteInt(const char *pname, const int *data, int count)
{
	BufferField(pname, sizeof(int) * count, (const char *)data);
}

void CSave::WriteFloat(const char *pname, const float *data, int count)
{
	BufferField(pname, sizeof(float) * count, (const char *)data);
}

void CSave::WriteTime(const char *pname, const float *data, int count)
{
	BufferHeader(pname, sizeof(float) * count);

	for (int i = 0; i < count; i++)
	{
		float tmp = data[0];

		if (m_pdata)
			tmp -= m_pdata->time;

		BufferData((const char *)&tmp, sizeof(float));
		data++;
	}
}

void CSave::WriteString(const char *pname, const char *pdata)
{
	BufferField(pname, strlen(pdata) + 1, pdata);
}

void CSave::WriteString(const char *pname, const int *stringId, int count)
{
	int i;
	int size = 0;

	for (i = 0; i < count; i++)
		size += strlen(STRING(stringId[i])) + 1;

	BufferHeader(pname, size);

	for (i = 0; i < count; i++)
	{
		const char *pString = STRING(stringId[i]);
		BufferData(pString, strlen(pString)+1);
	}
}

void CSave::WriteVector(const char *pname, const Vector &value)
{
	WriteVector(pname, &value.x, 1);
}

void CSave::WriteVector(const char *pname, const float *value, int count)
{
	BufferHeader(pname, sizeof(float) * 3 * count);
	BufferData((const char *)value, sizeof(float) * 3 * count);
}

void CSave::WritePositionVector(const char *pname, const Vector &value)
{
	if (m_pdata && m_pdata->fUseLandmark)
	{
		Vector tmp = value - m_pdata->vecLandmarkOffset;
		WriteVector(pname, tmp);
	}

	WriteVector(pname, value);
}

void CSave::WritePositionVector(const char *pname, const float *value, int count)
{
	BufferHeader(pname, sizeof(float) * 3 * count);

	for (int i = 0; i < count; i++)
	{
		Vector tmp(value[0], value[1], value[2]);

		if (m_pdata && m_pdata->fUseLandmark)
			tmp = tmp - m_pdata->vecLandmarkOffset;

		BufferData((const char *)&tmp.x, sizeof(float) * 3);
		value += 3;
	}
}

void CSave::WriteFunction(const char *pname, const int *data, int count)
{
	const char *functionName = NAME_FOR_FUNCTION(*data);

	if (functionName)
		BufferField(pname, strlen(functionName) + 1, functionName);
	else
		ALERT(at_error, "Invalid function pointer in entity!");
}

void EntvarsKeyvalue(entvars_t *pev, KeyValueData *pkvd)
{
	for (int i = 0; i < (int)(ENTVARS_COUNT); i++)
	{
		TYPEDESCRIPTION *pField = &gEntvarsDescription[i];

		if (!stricmp(pField->fieldName, pkvd->szKeyName))
		{
			switch (pField->fieldType)
			{
				case FIELD_MODELNAME:
				case FIELD_SOUNDNAME:
				case FIELD_STRING: (*(int *)((char *)pev + pField->fieldOffset)) = ALLOC_STRING(pkvd->szValue); break;
				case FIELD_TIME:
				case FIELD_FLOAT: (*(float *)((char *)pev + pField->fieldOffset)) = atof(pkvd->szValue); break;
				case FIELD_INTEGER: (*(int *)((char *)pev + pField->fieldOffset)) = atoi(pkvd->szValue); break;
				case FIELD_POSITION_VECTOR:
				case FIELD_VECTOR: UTIL_StringToVector((float *)((char *)pev + pField->fieldOffset), pkvd->szValue); break;

				default:
				case FIELD_EVARS:
				case FIELD_CLASSPTR:
				case FIELD_EDICT:
				case FIELD_ENTITY:
				case FIELD_POINTER: ALERT(at_error, "Bad field in entity!!\n"); break;
			}

			pkvd->fHandled = TRUE;
			return;
		}
	}
}

int CSave::WriteEntVars(const char *pname, entvars_t *pev)
{
	return WriteFields(pname, pev, gEntvarsDescription, ENTVARS_COUNT);
}

int CSave::WriteFields(const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount)
{
	int i;
	int emptyCount = 0;

	for (i = 0; i < fieldCount; i++)
	{
		void *pOutputData = ((char *)pBaseData + pFields[i].fieldOffset);

		if (DataEmpty((const char *)pOutputData, pFields[i].fieldSize * gSizes[pFields[i].fieldType]))
			emptyCount++;
	}

	int entityArray[MAX_ENTITYARRAY];
	int actualCount = fieldCount - emptyCount;
	WriteInt(pname, &actualCount, 1);

	for (i = 0; i < fieldCount; i++)
	{
		TYPEDESCRIPTION *pTest = &pFields[i];
		void *pOutputData = ((char *)pBaseData + pTest->fieldOffset);

		if (DataEmpty((const char *)pOutputData, pTest->fieldSize * gSizes[pTest->fieldType]))
			continue;

		switch (pTest->fieldType)
		{
			case FIELD_FLOAT: WriteFloat(pTest->fieldName, (float *)pOutputData, pTest->fieldSize); break;
			case FIELD_TIME: WriteTime(pTest->fieldName, (float *)pOutputData, pTest->fieldSize); break;
			case FIELD_MODELNAME:
			case FIELD_SOUNDNAME:
			case FIELD_STRING: WriteString(pTest->fieldName, (int *)pOutputData, pTest->fieldSize); break;
			case FIELD_CLASSPTR:
			case FIELD_EVARS:
			case FIELD_EDICT:
			case FIELD_ENTITY:
			case FIELD_EHANDLE:
			{
				if (pTest->fieldSize > MAX_ENTITYARRAY)
					ALERT(at_error, "Can't save more than %d entities in an array!!!\n", MAX_ENTITYARRAY);

				for (int j = 0; j < pTest->fieldSize; j++)
				{
					switch (pTest->fieldType)
					{
						case FIELD_EVARS: entityArray[j] = EntityIndex(((entvars_t **)pOutputData)[j]); break;
						case FIELD_CLASSPTR: entityArray[j] = EntityIndex(((CBaseEntity **)pOutputData)[j]); break;
						case FIELD_EDICT: entityArray[j] = EntityIndex(((edict_t **)pOutputData)[j]); break;
						case FIELD_ENTITY: entityArray[j] = EntityIndex(((EOFFSET *)pOutputData)[j]); break;
						case FIELD_EHANDLE: entityArray[j] = EntityIndex((CBaseEntity *)(((EHANDLE *)pOutputData)[j])); break;
						default: break;
					}
				}

				WriteInt(pTest->fieldName, entityArray, pTest->fieldSize);
				break;
			}

			case FIELD_POSITION_VECTOR: WritePositionVector(pTest->fieldName, (float *)pOutputData, pTest->fieldSize); break;
			case FIELD_VECTOR: WriteVector(pTest->fieldName, (float *)pOutputData, pTest->fieldSize); break;
			case FIELD_BOOLEAN:
			case FIELD_INTEGER: WriteInt(pTest->fieldName, (int *)pOutputData, pTest->fieldSize); break;
			case FIELD_SHORT: WriteData(pTest->fieldName, 2 * pTest->fieldSize, ((char *)pOutputData)); break;
			case FIELD_CHARACTER: WriteData(pTest->fieldName, pTest->fieldSize, ((char *)pOutputData)); break;
			case FIELD_POINTER: WriteInt(pTest->fieldName, (int *)(char *)pOutputData, pTest->fieldSize); break;
			case FIELD_FUNCTION: WriteFunction(pTest->fieldName, (int *)(char *)pOutputData, pTest->fieldSize); break;
			default: ALERT(at_error, "Bad field type\n");
		}
	}

	return 1;
}

void CSave::BufferString(char *pdata, int len)
{
	char c = 0;
	BufferData(pdata, len);
	BufferData(&c, 1);
}

int CSave::DataEmpty(const char *pdata, int size)
{
	for (int i = 0; i < size; i++)
	{
		if (pdata[i])
			return 0;
	}

	return 1;
}

void CSave::BufferField(const char *pname, int size, const char *pdata)
{
	BufferHeader(pname, size);
	BufferData(pdata, size);
}

void CSave::BufferHeader(const char *pname, int size)
{
	short hashvalue = TokenHash(pname);

	if (size > 1 << (sizeof(short) * 8))
		ALERT(at_error, "CSave::BufferHeader() size parameter exceeds 'short'!");

	BufferData((const char *)&size, sizeof(short));
	BufferData((const char *)&hashvalue, sizeof(short));
}

void CSave::BufferData(const char *pdata, int size)
{
	if (!m_pdata)
		return;

	if (m_pdata->size + size > m_pdata->bufferSize)
	{
		ALERT(at_error, "Save/Restore overflow!");
		m_pdata->size = m_pdata->bufferSize;
		return;
	}

	memcpy(m_pdata->pCurrentData, pdata, size);
	m_pdata->pCurrentData += size;
	m_pdata->size += size;
}

int CRestore::ReadField(void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount, int startField, int size, char *pName, void *pData)
{
	float time = 0;
	Vector position = Vector(0, 0, 0);

	if (m_pdata)
	{
		time = m_pdata->time;

		if (m_pdata->fUseLandmark)
			position = m_pdata->vecLandmarkOffset;
	}

	for (int i = 0; i < fieldCount; i++)
	{
		int fieldNumber = (i + startField) % fieldCount;
		TYPEDESCRIPTION *pTest = &pFields[fieldNumber];

		if (!stricmp(pTest->fieldName, pName))
		{
			if (!m_global || !(pTest->flags & FTYPEDESC_GLOBAL))
			{
				for (int j = 0; j < pTest->fieldSize; j++)
				{
					void *pOutputData = ((char *)pBaseData + pTest->fieldOffset + (j * gSizes[pTest->fieldType]));
					void *pInputData = (char *)pData + j * gSizes[pTest->fieldType];

					switch (pTest->fieldType)
					{
						case FIELD_TIME:
						{
							float timeData = *(float *)pInputData;
							timeData += time;
							*((float *)pOutputData) = timeData;
							break;
						}

						case FIELD_FLOAT: *((float *)pOutputData) = *(float *)pInputData; break;
						case FIELD_MODELNAME:
						case FIELD_SOUNDNAME:
						case FIELD_STRING:
						{
							char *pString = (char *)pData;

							for (int stringCount = 0; stringCount < j; stringCount++)
							{
								while (*pString)
									pString++;

								pString++;
							}

							pInputData = pString;

							if (!strlen((char *)pInputData))
								*((int *)pOutputData) = 0;
							else
							{
								int string = ALLOC_STRING((char *)pInputData);
								*((int *)pOutputData) = string;

								if (!FStringNull(string) && m_precache)
								{
									if (pTest->fieldType == FIELD_MODELNAME)
										PRECACHE_MODEL((char *)STRING(string));
									else if (pTest->fieldType == FIELD_SOUNDNAME)
										PRECACHE_SOUND((char *)STRING(string));
								}
							}

							break;
						}

						case FIELD_EVARS:
						{
							int entityIndex = *(int *)pInputData;
							edict_t *pent = EntityFromIndex(entityIndex);

							if (pent)
								*((entvars_t **)pOutputData) = VARS(pent);
							else
								*((entvars_t **)pOutputData) = NULL;

							break;
						}

						case FIELD_CLASSPTR:
						{
							int entityIndex = *(int *)pInputData;
							edict_t *pent = EntityFromIndex(entityIndex);

							if (pent)
								*((CBaseEntity **)pOutputData) = CBaseEntity::Instance(pent);
							else
								*((CBaseEntity **)pOutputData) = NULL;

							break;
						}

						case FIELD_EDICT:
						{
							int entityIndex = *(int *)pInputData;
							edict_t *pent = EntityFromIndex(entityIndex);
							*((edict_t **)pOutputData) = pent;
							break;
						}

						case FIELD_EHANDLE:
						{
							pOutputData = (char *)pOutputData + j * (sizeof(EHANDLE) - gSizes[pTest->fieldType]);
							int entityIndex = *(int *)pInputData;
							edict_t *pent = EntityFromIndex(entityIndex);

							if (pent)
								*((EHANDLE *)pOutputData) = CBaseEntity::Instance(pent);
							else
								*((EHANDLE *)pOutputData) = NULL;

							break;
						}

						case FIELD_ENTITY:
						{
							int entityIndex = *(int *)pInputData;
							edict_t *pent = EntityFromIndex(entityIndex);

							if (pent)
								*((EOFFSET *)pOutputData) = OFFSET(pent);
							else
								*((EOFFSET *)pOutputData) = 0;

							break;
						}

						case FIELD_VECTOR:
						{
							((float *)pOutputData)[0] = ((float *)pInputData)[0];
							((float *)pOutputData)[1] = ((float *)pInputData)[1];
							((float *)pOutputData)[2] = ((float *)pInputData)[2];
							break;
						}

						case FIELD_POSITION_VECTOR:
						{
							((float *)pOutputData)[0] = ((float *)pInputData)[0] + position.x;
							((float *)pOutputData)[1] = ((float *)pInputData)[1] + position.y;
							((float *)pOutputData)[2] = ((float *)pInputData)[2] + position.z;
							break;
						}

						case FIELD_BOOLEAN:
						case FIELD_INTEGER: *((int *)pOutputData) = *(int *)pInputData; break;
						case FIELD_SHORT: *((short *)pOutputData) = *(short *)pInputData; break;
						case FIELD_CHARACTER: *((char *)pOutputData) = *(char *)pInputData; break;
						case FIELD_POINTER: *((int *)pOutputData) = *(int *)pInputData; break;
						case FIELD_FUNCTION:
						{
							if (!strlen((char *)pInputData))
								*((int *)pOutputData) = 0;
							else
								*((int *)pOutputData) = FUNCTION_FROM_NAME((char *)pInputData);

							break;
						}

						default: ALERT(at_error, "Bad field type\n");
					}
				}
			}

			return fieldNumber;
		}
	}

	return -1;
}

int CRestore::ReadEntVars(const char *pname, entvars_t *pev)
{
	return ReadFields(pname, pev, gEntvarsDescription, ENTVARS_COUNT);
}

int CRestore::ReadFields(const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount)
{
	unsigned short i = ReadShort();
	unsigned short token = ReadShort();

	if (token != TokenHash(pname))
	{
		BufferRewind(2 * sizeof(short));
		return 0;
	}

	int fileCount = ReadInt();
	int lastField = 0;

	for (i = 0; i < fieldCount; i++)
	{
		if (!m_global || !(pFields[i].flags & FTYPEDESC_GLOBAL))
			memset(((char *)pBaseData + pFields[i].fieldOffset), 0, pFields[i].fieldSize * gSizes[pFields[i].fieldType]);
	}

	for (i = 0; i < fileCount; i++)
	{
		HEADER header;
		BufferReadHeader(&header);
		lastField = ReadField(pBaseData, pFields, fieldCount, lastField, header.size, m_pdata->pTokens[header.token], header.pData);
		lastField++;
	}

	return 1;
}

void CRestore::BufferReadHeader(HEADER *pheader)
{
	pheader->size = ReadShort();
	pheader->token = ReadShort();
	pheader->pData = BufferPointer();
	BufferSkipBytes(pheader->size);
}

short CRestore::ReadShort(void)
{
	short tmp = 0;
	BufferReadBytes((char *)&tmp, sizeof(short));
	return tmp;
}

int CRestore::ReadInt(void)
{
	int tmp = 0;
	BufferReadBytes((char *)&tmp, sizeof(int));
	return tmp;
}

int CRestore::ReadNamedInt(const char *pName)
{
	HEADER header;
	BufferReadHeader(&header);
	return ((int *)header.pData)[0];
}

char *CRestore::ReadNamedString(const char *pName)
{
	HEADER header;
	BufferReadHeader(&header);
	return (char *)header.pData;
}

char *CRestore::BufferPointer(void)
{
	if (!m_pdata)
		return NULL;

	return m_pdata->pCurrentData;
}

void CRestore::BufferReadBytes(char *pOutput, int size)
{
	if (!m_pdata || Empty())
		return;

	if ((m_pdata->size + size) > m_pdata->bufferSize)
	{
		ALERT(at_error, "Restore overflow!");
		m_pdata->size = m_pdata->bufferSize;
		return;
	}

	if (pOutput)
		memcpy(pOutput, m_pdata->pCurrentData, size);

	m_pdata->pCurrentData += size;
	m_pdata->size += size;
}

void CRestore::BufferSkipBytes(int bytes)
{
	BufferReadBytes(NULL, bytes);
}

int CRestore::BufferSkipZString(void)
{
	if (!m_pdata)
		return 0;

	int maxLen = m_pdata->bufferSize - m_pdata->size;
	int len = 0;
	char *pszSearch = m_pdata->pCurrentData;

	while (*pszSearch++ && len < maxLen)
		len++;

	len++;
	BufferSkipBytes(len);
	return len;
}

int CRestore::BufferCheckZString(const char *string)
{
	if (!m_pdata)
		return 0;

	int maxLen = m_pdata->bufferSize - m_pdata->size;
	int len = strlen(string);

	if (len <= maxLen)
	{
		if (!strncmp(string, m_pdata->pCurrentData, len))
			return 1;
	}

	return 0;
}

char UTIL_TextureHit(TraceResult *ptrHit, Vector vecSrc, Vector vecEnd)
{
	CBaseEntity *pentHit = CBaseEntity::Instance(ptrHit->pHit);

	if (pentHit && pentHit->Classify() != CLASS_NONE && pentHit->Classify() != CLASS_MOVEABLE)
		return CHAR_TEX_FLESH;

	float rgflSrc[3];
	vecSrc.CopyToArray(rgflSrc);

	float rgflEnd[3];
	vecEnd.CopyToArray(rgflEnd);

	char szBuffer[64];
	const char *pszTextureName;

	if (pentHit)
		pszTextureName = TRACE_TEXTURE(ENT(pentHit->pev), rgflSrc, rgflEnd);
	else
		pszTextureName = TRACE_TEXTURE(ENT(0), rgflSrc, rgflEnd);

	if (pszTextureName)
	{
		if (*pszTextureName == '-' || *pszTextureName == '+')
			pszTextureName += 2;

		if (*pszTextureName == '{' || *pszTextureName == '!' || *pszTextureName == '~' || *pszTextureName == ' ')
			pszTextureName++;

		strcpy(szBuffer, pszTextureName);
		szBuffer[CBTEXTURENAMEMAX - 1] = 0;
		return TEXTURETYPE_Find(szBuffer);
	}

	return NULL;
}

BOOL UTIL_IsHullDefault(Vector vecOrigin, const float BOUNDS, edict_t *pEnt)
{
	Vector traceEnds[8];
	TraceResult tr;
	traceEnds[0] = vecOrigin;
	traceEnds[0].x = traceEnds[0].x - BOUNDS;
	traceEnds[0].y = traceEnds[0].y - BOUNDS;
	traceEnds[0].z = traceEnds[0].z - BOUNDS;

	traceEnds[1] = vecOrigin;
	traceEnds[1].x = traceEnds[1].x - BOUNDS;
	traceEnds[1].y = traceEnds[1].y - BOUNDS;
	traceEnds[1].z = traceEnds[1].z + BOUNDS;

	traceEnds[2] = vecOrigin;
	traceEnds[2].x = traceEnds[2].x + BOUNDS;
	traceEnds[2].y = traceEnds[2].y - BOUNDS;
	traceEnds[2].z = traceEnds[2].z + BOUNDS;

	traceEnds[3] = vecOrigin;
	traceEnds[3].x = traceEnds[3].x + BOUNDS;
	traceEnds[3].y = traceEnds[3].y - BOUNDS;
	traceEnds[3].z = traceEnds[3].z - BOUNDS;

	traceEnds[4] = vecOrigin;
	traceEnds[4].x = traceEnds[4].x - BOUNDS;
	traceEnds[4].y = traceEnds[4].y + BOUNDS;
	traceEnds[4].z = traceEnds[4].z - BOUNDS;

	traceEnds[5] = vecOrigin;
	traceEnds[5].x = traceEnds[5].x - BOUNDS;
	traceEnds[5].y = traceEnds[5].y + BOUNDS;
	traceEnds[5].z = traceEnds[5].z + BOUNDS;

	traceEnds[6] = vecOrigin;
	traceEnds[6].x = traceEnds[6].x + BOUNDS;
	traceEnds[6].y = traceEnds[6].y + BOUNDS;
	traceEnds[6].z = traceEnds[6].z + BOUNDS;

	traceEnds[7] = vecOrigin;
	traceEnds[7].x = traceEnds[7].x + BOUNDS;
	traceEnds[7].y = traceEnds[7].y + BOUNDS;
	traceEnds[7].z = traceEnds[7].z - BOUNDS;

	for (int i = 0; i < 8; i++)
	{
		if (POINT_CONTENTS(traceEnds[i]) != CONTENTS_EMPTY)
			return FALSE;

		UTIL_TraceLine(vecOrigin, traceEnds[i], dont_ignore_monsters, pEnt, &tr);
		if (tr.flFraction != 1)
			return FALSE;
		if (traceEnds[i] != tr.vecEndPos)
			return FALSE;
	}
	return TRUE;
}

BOOL UTIL_IsHullDefaultEx(Vector vecOrigin, float size, float height, edict_t *pEnt)
{
	Vector traceEnds[8];
	TraceResult tr;
	traceEnds[0] = vecOrigin;
	traceEnds[0].x = traceEnds[0].x + size;
	traceEnds[0].y = traceEnds[0].y - size;
	traceEnds[0].z = traceEnds[0].z - height;

	traceEnds[1] = vecOrigin;
	traceEnds[1].x = traceEnds[1].x - size;
	traceEnds[1].y = traceEnds[1].y - size;
	traceEnds[1].z = traceEnds[1].z + height;

	traceEnds[2] = vecOrigin;
	traceEnds[2].x = traceEnds[2].x + size;
	traceEnds[2].y = traceEnds[2].y - size;
	traceEnds[2].z = traceEnds[2].z + height;

	traceEnds[3] = vecOrigin;
	traceEnds[3].x = traceEnds[3].x + size;
	traceEnds[3].y = traceEnds[3].y - size;
	traceEnds[3].z = traceEnds[3].z - height;

	traceEnds[4] = vecOrigin;
	traceEnds[4].x = traceEnds[4].x - size;
	traceEnds[4].y = traceEnds[4].y + size;
	traceEnds[4].z = traceEnds[4].z - height;

	traceEnds[5] = vecOrigin;
	traceEnds[5].x = traceEnds[5].x - size;
	traceEnds[5].y = traceEnds[5].y + size;
	traceEnds[5].z = traceEnds[5].z + height;

	traceEnds[6] = vecOrigin;
	traceEnds[6].x = traceEnds[6].x + size;
	traceEnds[6].y = traceEnds[6].y + size;
	traceEnds[6].z = traceEnds[6].z + height;

	traceEnds[7] = vecOrigin;
	traceEnds[7].x = traceEnds[7].x + size;
	traceEnds[7].y = traceEnds[7].y + size;
	traceEnds[7].z = traceEnds[7].z - height;

	for (int i = 0; i < 8; i++)
	{
		if (POINT_CONTENTS(traceEnds[i]) != CONTENTS_EMPTY)
			return FALSE;

		//server only
		UTIL_TraceLine(vecOrigin, traceEnds[i], dont_ignore_monsters, pEnt, &tr);
		if (tr.flFraction != 1)
			return FALSE;
		if (traceEnds[i] != tr.vecEndPos)
			return FALSE;
	}
	return TRUE;
}

float UTIL_GetPlayerGaitYaw(int playerIndex)
{
	CBasePlayer *pPlayer = NULL;

	if (playerIndex > 0 && playerIndex <= gpGlobals->maxClients)
	{
		edict_t *pPlayerEdict = INDEXENT(playerIndex);

		if (pPlayerEdict && !pPlayerEdict->free)
		{
			pPlayer = (CBasePlayer *)CBaseEntity::Instance(pPlayerEdict);

			if (pPlayer)
				return pPlayer->m_flGaitYaw;
		}
	}

	return 0;
}

BOOL UTIL_IsHullInZone(edict_t *e, Vector vecMins, Vector vecMaxs)
{
	if(!e->v.modelindex || e->v.modelindex < 0 || e->v.modelindex > 511)
		return FALSE;

	if ((vecMins.x > e->v.absmax.x) || (vecMins.y > e->v.absmax.y) || (vecMins.z > e->v.absmax.z) || (vecMaxs.x < e->v.absmin.x) || (vecMaxs.y < e->v.absmin.y) || (vecMaxs.z < e->v.absmin.z))
		return FALSE;

	model_t *mod = sv_models[e->v.modelindex];

	if(mod->type != mod_brush)
		return FALSE;

	Vector vecOrigin;

	hull_t *hull = &mod->hulls[0];

	for(int i = 0; i < 8; ++i)
	{
		vecOrigin.x = (i & 1) ? vecMins.x : vecMaxs.x;
		vecOrigin.y = (i & 2) ? vecMins.y : vecMaxs.y;
		vecOrigin.z = (i & 4) ? vecMins.z : vecMaxs.z;
		if(gSVFuncs.SV_HullPointContents(hull, hull->firstclipnode, vecOrigin) == CONTENT_SOLID)
			return TRUE;
	}

	return FALSE;
}